/** @file GOTHgpio.c This file contains source of the API for GPIO of the
 * GOTH Kick-ass Etherboard.
 * Currently, the following hardware is supported:
 *	- the 2 push buttons
 *  - the 2 dip switches
 *	- the 8 leds
 *
 * @author Geoffrey Ottoy - Tom Hamelinckx
 * @date 05/07/2011 (last modified)
 *
 * <pre>
 * MODIFICATION HISTORY:
 *
 * Who  Date     Changes
 * ---- -------- ---------------------------------------------------------
 * GO   30/06/11 Created this file based on a previous file by TH.
 * GO   01/07/11 Added BOTH_BTNS support to GPIO_DisableBtns() and
 *               GPIO_EnableBtns().
 * GO   01/07/11 Added toggle leds and clear leds functionality.
 * GO   05/07/11 Changed access to output pins from PORTx to LATx
 * GO   06/07/11 Added extra definitions for the leds
 * </pre>
 * 
 */

#include "GOTHgpio.h"

#include "GOTHuart.h"
#include "stdio.h"

/* All the functionality for the push buttons */

volatile int btn1_pressed, btn2_pressed;

/** Initialize the dip switches for operation
 * @param	button_nr	Determines which button is initialized. Choose
 *						between BTN_1, BTN_2 and BOTH_BTNS.
 * @return	nothing
 */
void GPIO_InitBtns(unsigned int button_nr){

	// Unlock Registers
	//	__builtin_write_OSCCONL(OSCCON & 0xbf);
	//HMX: we lost "unlocking" of registers, but seems to work? Check!
	//GO: don't use this or the uart operation fails
	INTCON2bits.INT1EP = FALLING;
	INTCON2bits.INT2EP = FALLING;
	
	// Configure Input Functions **********************
	switch(button_nr){
		case BTN_1:{
			TRISFbits.TRISF12 = 1;		// input
			CNPU5bits.CN75PUE = 0;		// pull-up disabled
			CNPD5bits.CN75PDE = 0;		// pull-down disabled
			RPINR0bits.INT1R = 32;		//map int1 to rp32 (btn1)
		//	IFS1bits.INT1IF = 0;  		//Clear int1 irpt-flag
		} break;
		case BTN_2:{
			TRISFbits.TRISF13 = 1;		// input
			CNPU5bits.CN76PUE = 0;		// pull-up disabled
			CNPD5bits.CN76PDE = 0;		// pull-down disabled
			RPINR1bits.INT2R = 31;		// map int2 to rp31 (btn2)
		//	IFS1bits.INT2IF = 0;		// Clear int2 iprt-flag
		} break;
		case BOTH_BTNS:{
			TRISFbits.TRISF12 = 1;		// input
			CNPU5bits.CN75PUE = 0;		// pull-up disabled
			CNPD5bits.CN75PDE = 0;		// pull-down disabled
			RPINR0bits.INT1R = 32;		//map int1 to rp32 (btn1)
		//	IFS1bits.INT1IF = 0; 		//Clear int1 irpt-flag

			TRISFbits.TRISF13 = 1;		// input
			CNPU5bits.CN76PUE = 0;		// pull-up disabled
			CNPD5bits.CN76PDE = 0;		// pull-down disabled
			RPINR1bits.INT2R = 31;		//map int2 to rp31 (btn2)
		//	IFS1bits.INT2IF = 0;		//Clear int2 iprt-flag
		} break;
		default: break;
	}
	
	// Init status vars
	btn1_pressed = 0;
	btn2_pressed = 0;
	
	///@todo Set interrupt priority if necessary
	
	// Lock Registers
	//HMX: is this necessary?!
	//GO: no
	//	__builtin_write_OSCCONL(OSCCON | 0x40);

}

/** Enables the buttons interrupt (INT1 resp. INT2). 
 * @param	button_nr	Determines which button interrupt is turned off. Choose
 *						between BTN_1, BTN_2 and BOTH_BTNS.
 * @return	nothing
 */
void GPIO_EnableBtns(unsigned int button_nr){
	switch(button_nr){
		case BTN_1:{
 			IEC1bits.INT1IE = 1; // Enable INT1
			IFS1bits.INT1IF = 0; // Clear flag
		} break;
		case BTN_2:{
			IEC1bits.INT2IE = 1; // Enable INT2
			IFS1bits.INT2IF = 0; // Clear flag
		}break;
		case BOTH_BTNS:{
			IEC1bits.INT1IE = 1; // Enable INT1
			IFS1bits.INT1IF = 0; // Clear flag
			IEC1bits.INT2IE = 1; // Enable INT2
			IFS1bits.INT2IF = 0; // Clear flag
		}break;
		default: break;
	}
}

/** Disables the buttons interrupt (INT1 resp. INT2). 
 * @param	button_nr	Determines which button interrupt is turned off. Choose
 *						between BTN_1, BTN_2 and BOTH_BTNS.
 * @return	nothing
 */
void GPIO_DisableBtns(unsigned int button_nr){
	switch(button_nr){
		case BTN_1:{
 			IEC1bits.INT1IE = 0; // Disable INT1
			IFS1bits.INT1IF = 0;
		} break;
		case BTN_2:{
			IEC1bits.INT2IE = 0; // Disable INT1
			IFS1bits.INT2IF = 0;
		} break;
		case BOTH_BTNS:{
 			IEC1bits.INT1IE = 0; // Disable INT1
			IFS1bits.INT1IF = 0;
			IEC1bits.INT2IE = 0; // Disable INT1
			IFS1bits.INT2IF = 0;
		} break;
		default: break;
	}
}

/** Check if BTN_1 was pressed.
 *
 * @return	The number of button presses. 
 */
int GPIO_Btn1Pressed(){
	int ret_val = btn1_pressed;
	btn1_pressed = 0; // clear status variable so a new button press will be recognized
	return ret_val;
}

/** Check if BTN_2 was pressed.
 *
 * @return	The number of button presses.
 */
int GPIO_Btn2Pressed(){
	int ret_val = btn2_pressed;
	btn2_pressed = 0; // clear status variable so a new button press will be recognized
	return ret_val;
}

/** The INT1 isr (which is associated with Button 1)
 *
 * @return	nothing
 */
void __attribute__((interrupt,no_auto_psv)) _INT1Interrupt(void){	
	// set status variable
	btn1_pressed++;
	
	/// @todo	you can add application specific code here
	
	IFS1bits.INT1IF = 0;    //Clear the INT1 interrupt flag or else
                            //the CPU will keep vectoring back to the ISR
}

/** The INT2 isr (which is associated with Button 2)
 *
 * @return	nothing
 */
void __attribute__((interrupt,no_auto_psv)) _INT2Interrupt(void){
	// set status variable
	btn2_pressed++;
	
	/// @todo	you can add application specific code here
	
	IFS1bits.INT2IF = 0;    //Clear the INT0 interrupt flag or else
                            //the CPU will keep vectoring back to the ISR
}

/* All the functionality for the dip switches */

/** Initialize the dip switches for operation
 *
 * @return	nothing
 */
void GPIO_InitDips(){
	// DIPS at RC13 en RC14, set pins as input
	TRISCbits.TRISC13 = 1;
	TRISCbits.TRISC14 = 1;
	// Disable pull-ups and pull-downs
	CNPU1bits.CN0PUE = 0;
	CNPD1bits.CN0PDE = 0;
	CNPU1bits.CN1PUE = 0;
	CNPD1bits.CN1PDE = 0;
}

/** Get the current value of the dip switches
 *
 * @return	The current value of the dip switches (1 = on, 0 = off)
 */
unsigned int GPIO_GetDipsValue(){
	// value needs to be inverted (on = '0', off = '1')
	unsigned int value = ~PORTC & 0x6000; // invert + apply bit mask
	return (value >> 13); // shift to b1 and b0
}

/* All the functionality for the leds */

/** Initialize the leds for operation
 *
 * @return	nothing
 */
void GPIO_InitLeds(){
	// leds are PORTE7-0
	TRISE &= 0xff00;
	LATE &= 0xff00;
}

/** Turn a led on or set the value of all leds
 *
 * @param	identifier	This is the new value for the leds (1 = on, 0 = off). Alternatively one can
 *						do something like this:
 *						GPIO_SetLeds(RED_LED_0 | GREEN_LED_2 | GREEN_LED_1);
 * @return	nothing
 */
void GPIO_SetLeds(unsigned char identifier){
	LATE &= 0xff00;
	LATE |= identifier;
}

/** Toggle the leds
 *
 * @param	identifier	This determines which leds will be toggled.
 * @return	nothing
 */
void GPIO_ToggleLeds(unsigned char identifier){
	unsigned int current_value = LATE;
	current_value ^= (identifier & 0x00ff);
	LATE = current_value;
}

/** Toggle the leds
 *
 * @param	identifier	This determines which leds will be cleared.
 * @return	nothing
 */
void GPIO_ClearLeds(unsigned char identifier){
	unsigned int current_value = LATE;
	current_value &= (0xff00 | ~identifier);
	LATE = current_value;
}
